#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>

unsigned int major;
static struct class *linus_class;
static int g_key;

struct gpio_key{
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
	struct work_struct work;
};

//DECLARE_WORK

struct gpio_key *keyv;

static void key_work_func(struct work_struct *work)
{
	struct gpio_key *key = container_of(work, struct gpio_key, work);

	
    printk("%s -> %d\n", __FUNCTION__, key->gpio);
}


static irqreturn_t linus_irq(int irqid, void *v)
{
	struct gpio_key *key = (struct gpio_key *)v;
	schedule_work(&key->work);

	return IRQ_HANDLED;
}

static int chrdev_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t chrdev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
{

	copy_to_user(user_buf, &g_key, 4);

	return 4;
}

static const struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = chrdev_open,
	.read = chrdev_read,
	
};

static int gpio_probe(struct platform_device *pdev)
{
	struct device_node *child = NULL;

/*
linus {
	compatible = "linus,gpio";
	pinctrl-names = "default";
	pinctrl-0 = <&linus_gpio>;
	gpi-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH
				&gpio2 9 GPIO_ACTIVE_HIGH
				&gpio2 12 GPIO_ACTIVE_HIGH
				&gpio2 15 GPIO_ACTIVE_HIGH>;

};


*/
	//for_each_child_of_node(pdev->dev.of_node, child)
	{
		int count = of_gpio_named_count(pdev->dev.of_node, "gpi-gpios");
		int num = 0;

		printk("count = %d\n", count);
		
		keyv = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
		
		for(; num<count; num++)
		{
			keyv[num].gpio = of_get_named_gpio(pdev->dev.of_node, "gpi-gpios", num);
			
			printk("gpio %d = %d\n", num, keyv[num].gpio);
			
			keyv[num].gpiod = gpio_to_desc(keyv[num].gpio);

			keyv[num].irq = gpiod_to_irq(keyv[num].gpiod);
			
			request_irq(keyv[num].irq, linus_irq, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "linus", &keyv[num]);

			INIT_WORK(&keyv[num].work, key_work_func);
		}
	}

/*start: 在/dev/下创建设备节点*/
	major = register_chrdev(0, "linus", &ops);
	linus_class = class_create(THIS_MODULE, "linus_class");

	if (IS_ERR(linus_class))
	{
		unregister_chrdev(major, "linus");
		return -1;
	}

	device_create(linus_class, NULL, MKDEV(major, 0), NULL, "linus");
/*end:*/	
	return 0;
}

static int gpio_remove(struct platform_device *pdev)
{
	int count = of_gpio_named_count(pdev->dev.of_node, "gpi-gpios");
	int i = 0;

	printk("driver removed %d\n", count);
	
	device_destroy(linus_class, MKDEV(major, 0));
	class_destroy(linus_class);
	unregister_chrdev(major, "linus");
	
	for (; i<count; i++)
	{
		free_irq(keyv[i].irq, &keyv[i]);
	}
	
	kfree(keyv);

	return 0;
}

static struct of_device_id gpio_keys_of_match[] = {
	{ .compatible = "linus,gpio", },
	{ },
};

static struct platform_driver gpio_keys_device_driver = {
	.probe		= gpio_probe,
	.remove 	= gpio_remove,
	.driver		= {
		.name	= "linus",
		.owner	= THIS_MODULE,
//		.pm	= &gpio_keys_pm_ops,
		.of_match_table = of_match_ptr(gpio_keys_of_match),
	}
};

static int __init linus_init(void)
{
	return platform_driver_register(&gpio_keys_device_driver);
}

static void __exit linus_exit(void)
{
	platform_driver_unregister(&gpio_keys_device_driver);
}

late_initcall(linus_init);
module_exit(linus_exit);

MODULE_LICENSE("GPL");




